/*
 * GlucoseServer.java
 *
 * Created on March 17, 2006, 10:27 AM
 *
 * To change this template, choose Tools | Options and locate the template under
 * the Source Creation and Management node. Right-click the template and choose
 * Open. You can then make changes to the template in the Source Editor.
 */

package Comm.Glucose;

import Experience.ExpUI;
import Comm.Util.Logger;
import javax.bluetooth.*;
import javax.microedition.io.*;
import java.io.*;
import java.util.Enumeration;

import javax.microedition.midlet.MIDlet;
import java.util.Hashtable;
import java.util.Vector;

/**
 *
 * @author dmgreen
 */
public class GlucoseServer implements Runnable {
    
    public final static UUID spp_uuid = new UUID("1101", false);
    public StreamConnectionNotifier server;
    
    private final static int SERVICE_TELEPHONY = 0x400000;
    
    private boolean DONE = false;

    private Hashtable myScreenCodes = new Hashtable();
    private Vector myScreenCache = new Vector();
       
    // Bluetooth singleton object
    LocalDevice device;
    DiscoveryAgent agent;
    
    DataOutputStream out; 
    
    /** Creates a new instance of GlucoseServer */
    public GlucoseServer() {
         
    }
    
    public void log(String msg) {
        Logger.getLog().addLogMessage(msg, Logger.MSG_DEBUG);
    }
    
    private Integer parseGlucoLine(String gluco_data) throws Exception {
        int comma_idx = gluco_data.indexOf(",");
        if (comma_idx < 0) {
            return null;
        }
        
        int next_comma_idx = gluco_data.indexOf(",", comma_idx+1);
        if (next_comma_idx < 0) {
            return null;
        }
        
        String reading = gluco_data.substring(comma_idx+1
                ,next_comma_idx);
        //System.out.println("1. READING IS \"" + reading + "\"");
        reading = reading.replace('\"', ' ');
        //System.out.println("2. READING IS \"" + reading + "\"");
        if ( ((String) reading.trim()).equals("")) {
            return null;
        }
        else {
            try {
                return Integer.valueOf(reading.trim());
            } catch (Exception e) {
                return null;
            }
        }
    }
    
    public void start_server() {
        try
        {
            //
            // initialize the JABWT stack
            device = LocalDevice.getLocalDevice(); // obtain reference to singleton
            device.setDiscoverable(DiscoveryAgent.GIAC); // set Discover mode to LIAC
      
             // start a thread to serve the server connection.
            // for simplicity of this demo, we only start one server thread
            // see run() for the task of this thread
            DONE = false;
            Thread t = new Thread( this );
            t.start();

        } catch ( BluetoothStateException e )
        {
            e.printStackTrace();
        }
    }
    
    
    public void stop_server() {
                
        if (server != null) {
            try {
                DONE = true;
                log("Closing server ... ");
                try {
                    server.close();
                } catch (Exception e) {
                    log("Unexpected exception in here ...");
                }
                log("Successfully closed server :)");
                server = null;
            } catch (Exception e) {
                log("Error while closing server :( " + e.toString());
            }
        }
    }
    
    public void toggleServer() {
        if (server == null) {
            start_server();
        }
        else{
            stop_server();
            log("Well ... made it out here!");
        }
        
    }
    
    public void send_char() {
        if (out != null) {
            try {
                out.writeChar(4);
                out.flush();
                log("Successfully wrote char!");
            }
            catch (IOException ioe) {
                log("Got an IOException while writing char!");
            }
        } else {
           log("DataOutputStream was NULL!");
        }

    }
    
    public Vector getScreenCache() {
        return myScreenCache;
    }
    
    public void clearScreenCache() {
        myScreenCache.removeAllElements();
    }
    
    public void run()
    {
    String appName = "GlucoseServer";


    // connection to remote device
    StreamConnection c = null;
    DataInputStream in; 
    
    try
    {
      String url = "btspp://localhost:" + spp_uuid.toString() +";authenticate=false;encrypt=false;name="+ appName;
      server =  (StreamConnectionNotifier)Connector.open( url );
      // Retrieve the service record template
      ServiceRecord rec = device.getRecord( server );
      
      // set ServiceRecrod ServiceAvailability (0x0008) attribute to indicate our service is available
      // 0xFF indicate fully available status
      // This operation is optional
      rec.setAttributeValue( 0x0008, new DataElement( DataElement.U_INT_1, 0xFF ) );
      
      DataElement de = rec.getAttributeValue(0x0004);
      
      int data_type = de.getDataType();
      //log("Top level data type is " + data_type);
      if (data_type == DataElement.DATSEQ || data_type == DataElement.DATSEQ) {
          Enumeration en = (Enumeration) de.getValue();
          
          while (en.hasMoreElements()) {
              DataElement next_dt = (DataElement) en.nextElement();

              if (next_dt.getDataType() == DataElement.U_INT_1) {
                  //log("type for this one is " + Integer.toString(next_dt.getDataType()));
                  Integer next_dt_value = (Integer) next_dt.getValue();
                  //log("value for this one is " + next_dt_value.toString());
              }
              else {
                  //log("type for this one is " + Integer.toString(next_dt.getDataType()));
              }
              
              
          }
      }
      
      // Set the Major Service Classes flag in Bluetooth stack.
      // We choose Telephony Service
      rec.setDeviceServiceClasses( SERVICE_TELEPHONY  );



     } catch (Exception e) {
          e.printStackTrace();
      }

    while(!DONE)
    {
        try {
            log("Looking for Connection");
            log("Ctrl-D is " + (char)4);
            c = server.acceptAndOpen();
            log("GOT CONNECTED");
            RemoteDevice rdev = RemoteDevice.getRemoteDevice( c );
            in  = c.openDataInputStream();
            out = c.openDataOutputStream();
        }
        catch (IOException ioe) {
            log("GOT IOException");
            continue;
        }
        
       StringBuffer string_buffer = new StringBuffer();
       Vector gluco_readings = new Vector();
       String lines_i_got = ""; 
       byte[] byte_buffer = new byte[20];
       
        while(true) {
            try {
              
                byte_buffer = new byte[20];
                int bytes_read = in.read(byte_buffer);
                log("Read " + bytes_read + " bytes: " + new String(byte_buffer));
                
                string_buffer.append( new String(byte_buffer).substring(0,bytes_read));
                
                String buf_as_string = string_buffer.toString();
                
                 while (buf_as_string.indexOf("\r\n") >= 0) {
                    int nln_loc = buf_as_string.indexOf("\r\n");
                     //log("Found a newline at " + nln_loc);
                    
                    String data_line = buf_as_string.substring(0,nln_loc);
                    //log("Line is " + data_line);
                    //System.out.println("GOT A LINE: " + data_line);
                    string_buffer.delete(0,nln_loc+2);
                    buf_as_string = string_buffer.toString();
                    
                    lines_i_got = lines_i_got + data_line + "\n";
                    
                    Integer glucose_reading = parseGlucoLine(data_line);
                    if (glucose_reading != null) {
                        gluco_readings.addElement(glucose_reading);
                        lines_i_got += glucose_reading + "\n";
                        
                    }
                 }  
            }
            catch (Exception e) {
                // means that the connection was probably interrupted
                // i.e. the monitor was turned off.
              
                log("Got an exception!");
                
                if (e.getClass().getName().indexOf("BluetoothConnectionException") >= 0 ) {
                    
                    log("It's an IOException!");
                    
                    //need to do something about leftover bytes in byte_buffer here!
                    // probably should make a 'get line' function
                    String the_rest = new String(byte_buffer);
                    int last_nln_loc = the_rest.indexOf("\r\n");
                    if (last_nln_loc >= 0) {
                        the_rest = the_rest.substring(0,last_nln_loc);
                        String last_line = string_buffer.toString() + the_rest;
                        try {
                            Integer last_reading = parseGlucoLine(last_line);            
                            if (last_reading != null) {
                                log("ONE LAST READING!");
                                gluco_readings.addElement(last_reading);
                            }
                        }
                        catch (Exception e2) {}
                    }
                    
                     // to see what data you were getting
                    //((ExpUI) myMIDlet).display.setCurrent(new Form(Integer.toString(screenArray.length)));
                
                    // Brrrrrring Brrrrrrrring !!
                
                    ExpUI.getMIDlet().startRinging(); 
                    // should you do all the clean up stuff here (from the last sample?
                    // no - probably better to have done it when you clean up - preserve resources
                    ExpUI.getMIDlet().startNewSample();
                    break;
               }
                else {
                    log("It's NOT an IOException!");
                    string_buffer.delete(0,string_buffer.length());
                    log("It's an " + e.getClass().getName());
                } 
                
            }
        } 
        
        // close current connection, wait for the next one
        try {
            c.close();
        }
        catch (IOException ioe) {continue;}
    
       log("LINES I GOT \n" + lines_i_got);
       
        if (gluco_readings.size() > 0) {
           log("GOT " + gluco_readings.size() + " readings:");
           
           //StringBuffer gluco_str_buf = new StringBuffer();
           
           for (int i = 0; i<gluco_readings.size(); i++) {
                log( String.valueOf((Integer)gluco_readings.elementAt(i)) );
                //gluco_str_buf.append(String.valueOf((Integer)gluco_readings.elementAt(i)) + " ");
           }
           
           boolean has_countdown = true;
           for (int i=1; i<11; i++) {
               Integer cnt_dwn = new Integer(i);
               
               if ( !(gluco_readings.indexOf(cnt_dwn) >= 0) ) {
                   has_countdown = false;
                   break;
               }
           }
           
           if (has_countdown) {
               Integer last_element = (Integer) gluco_readings.elementAt(gluco_readings.size()-1);
               ExpUI.getMIDlet().addGlucoseToSample(last_element.intValue());
           }   
        }
    }
    log("Exited while loop; thread joining ... ");
  }
} // while
